using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace org.synthful.Drawing
{
    /// <summary>
    /// Offered under Apache Public Licence 2.0
    /// synthful.org
    /// blessedgeek [@] synthful.org
    /// 
    /// Description:
    /// Allows suspension of painting to be requested in a multithreaded env.
    /// </summary>
    public class WMRedrawableControl : UserControl
    {
        protected WMRedraw WMRedrawing;

        public bool SuspendPainting
        {
            get
            {
                if (this.WMRedrawing == null)
                    return false;

                return this.WMRedrawing.SuspendPainting;
            }
            set
            {
                if (this.WMRedrawing != null)
                    this.WMRedrawing.SuspendPainting = value;
            }
        }

        public void setDrawStyle()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.CacheText, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.DoubleBuffered = true;
        }

    }

    public class WMRedraw
    {
        /// <summary>
        /// Offered under Apache Public Licence 2.0
        /// synthful.org
        /// blessedgeek [@] synthful.org
        /// 
        /// Description:
        /// Allows suspension of painting to be requested in a multithreaded env.
        /// </summary>
        public WMRedraw(Control c)
        {
            this.Control = c;
        }

        private Control Control;
        public event EventHandler SuspendPaintingBegin;
        public event EventHandler SuspendPaintingEnd;

        private const int WM_SETREDRAW = 11;
        //[DllImport("User32", EntryPoint = "SendMessage", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        //private static extern bool SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg,
            IntPtr wParam, IntPtr lParam);
        static private bool trySendMessage(IntPtr hWnd, int msg, int wParam, int lParam)
        {
            try
            {
                SendMessage(hWnd, (uint)msg, (IntPtr)wParam, (IntPtr)lParam);
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        private int _SuspendPainting = 0;
        /// <summary> 
        /// Stop painting while multiple changes are made to this and/or nested displayedControls. 
        /// </summary> 
        /// <value>True to suspend painting. False to resume painting.</value> 
        /// <remarks>Increments a counter each time flag is set true and decrements the 
        /// counter when set to false.
        /// This is to prevent sending any second consecutive suspension
        /// </remarks> 
        public bool SuspendPainting
        {
            get { return _SuspendPainting > 0; }
            set
            {
                if ((!this.Control.Visible || !this.Control.IsHandleCreated))
                    return; 

                if ((value))
                {
                    _SuspendPainting += 1;
                    if ((System.Threading.Interlocked.Increment(ref _SuspendPainting) == 1))
                    {
                        if (this.SuspendPaintingBegin != null)
                            this.SuspendPaintingBegin(this.Control, new EventArgs());
                        trySendMessage(this.Control.Handle, WM_SETREDRAW, 0, 0);
                    }
                }
                else
                {
                    _SuspendPainting = 1;
                    if ((System.Threading.Interlocked.Decrement(ref _SuspendPainting) == 0))
                    {
                        trySendMessage(this.Control.Handle, WM_SETREDRAW, 1, 0);
                        if (this.SuspendPaintingEnd != null)
                            this.SuspendPaintingEnd(this.Control, new EventArgs());
                    }
                }
            }
        }
    }
}
